using IQIGame.Onigao.Game;
using System.Collections.Generic;

namespace IQIGame.Onigao.Logic
{
    public class FixedBVHTree2D<T> where T : IFixedBVHObject2D
    {
        private const int MaxObjectsPerNode = 5;
        private const int MaxLevels = 8;
        private const int InitDepth = 3;

        public FixedBVHBounds2D bounds { private set; get; }

        public FixedBVHTree2D<T> left { private set; get; }

        public FixedBVHTree2D<T> right { private set; get; }

        private int _level;
        private List<T> _storeObjects;

        public FixedBVHTree2D(FixedBVHBounds2D bounds)
        {
            this.bounds = bounds;
            this._level = 0;
            _storeObjects = new List<T>();
            // 是root，先做几次拆分
            InitSplit(this, InitDepth);
        }

        public FixedBVHTree2D(FixedBVHBounds2D bounds, int level = 0)
        {
            this.bounds = bounds;
            this._level = level;
            _storeObjects = new List<T>();
        }

        private void InitSplit(FixedBVHTree2D<T> node, int deepth)
        {
            if (deepth <= 0)
            {
                return;
            }
            node.Split();
            InitSplit(node.left, deepth - 1);
            InitSplit(node.right, deepth - 1);
        }

        private void Split()
        {
            // 按照最长的轴进行切割
            var bmin = bounds.min;
            var bmax = bounds.max;
            var size = bmax - bmin;
            var mid = bmin + size / 2;

            Vector2Logic leftMax, rightMin;

            if (size.x > size.y)
            {
                leftMax = new Vector2Logic(mid.x, bmax.y);
                rightMin = new Vector2Logic(mid.x, bmin.y);
            }
            else
            {
                leftMax = new Vector2Logic(bmax.x, mid.y);
                rightMin = new Vector2Logic(bmin.x, mid.y);
            }

            int newLevel = this._level + 1;
            left = new FixedBVHTree2D<T>(new FixedBVHBounds2D(bmin, leftMax), newLevel);
            right = new FixedBVHTree2D<T>(new FixedBVHBounds2D(rightMin, bmax), newLevel);

        }

        public void Insert(T objectToInsert)
        {
            var objectBounds = objectToInsert.bvhBounds;
            if (left != null)
            {
                if (left.IntersectBounds(objectBounds))
                {
                    left.Insert(objectToInsert);
                }
                if (right.IntersectBounds(objectBounds))
                {
                    right.Insert(objectToInsert);
                }
                return;
            }

            _storeObjects.Add(objectToInsert);
            if (_storeObjects.Count > MaxObjectsPerNode && this._level < MaxLevels)
            {
                Split();

                //Reallocate this quads objects into its children
                for (int i = 0; i < _storeObjects.Count; ++i)
                {
                    T storedObj = _storeObjects[i];
                    var storedObjBounds = storedObj.bvhBounds;
                    if (left.IntersectBounds(storedObjBounds))
                    {
                        left.Insert(storedObj);
                    }
                    if (right.IntersectBounds(storedObjBounds))
                    {
                        right.Insert(storedObj);
                    }
                }

                //有子节点 本身不在存储
                _storeObjects.Clear();
            }
        }

        public void Remove(T objectToRemove)
        {
            // If the node is a leaf node,
            // remove the object from the node's object list
            if (left == null)
            {
                _storeObjects.Remove(objectToRemove);
                return;
            }

            // If the node is not a leaf node,
            // try to remove the object from its children
            var objectBounds = objectToRemove.bvhBounds;
            if (left != null && left.IntersectBounds(objectBounds))
            {
                left.Remove(objectToRemove);
            }
            if (right != null && right.IntersectBounds(objectBounds))
            {
                right.Remove(objectToRemove);
            }
        }

        public void Update(T objectToUpdate, FixedBVHBounds2D newBounds)
        {
            if (objectToUpdate.bvhBounds.IsEqual(newBounds))
            {
                return;
            }
            Remove(objectToUpdate);
            objectToUpdate.bvhBounds = newBounds;
            Insert(objectToUpdate);
        }

        public void RetrieveObjects(List<T> results, FixedBVHBounds2D queryBounds)
        {
            if (!this.IntersectBounds(queryBounds))
            {
                return;
            }

            if (left == null)
            {
                // If the node is a leaf node,
                // check each object in the node's object list
                for (int i = 0; i < _storeObjects.Count; i++)
                {
                    var obj = _storeObjects[i];
                    if (obj.bvhBounds.Intersects(queryBounds))
                    {
                        results.Add(obj);
                    }
                }
            }
            else
            {
                // If the node is not a leaf node,
                // query its children
                if (left != null && left.IntersectBounds(queryBounds))
                {
                    left.RetrieveObjects(results, queryBounds);
                }
                if (right != null && right.IntersectBounds(queryBounds))
                {
                    right.RetrieveObjects(results, queryBounds);
                }
            }
        }

        private bool IntersectBounds(FixedBVHBounds2D other)
        {
            return bounds.Intersects(other);
        }
    }

}